#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include "list/linked_list.h"
#include "gui/gui.h"

typedef struct LayoutData
{
    LinkedList *children;
} LayoutData;

static int LayoutDestroyData(LayoutData *data)
{
    if (!data)
        return -1;

    LinkedListDestroy(data->children);
    free(data);
    return 0;
}

static int LayoutMeasureChild(Layout *layout, View *childView, int *available_w, int *available_h, int *wrap_w, int *wrap_h)
{
    if (!layout || !childView)
        return -1;

    int child_occupy_w = 0;
    int child_occupy_h = 0;

    if (childView->visibility == TYPE_LAYOUT_VISIBILITY_GONE)
    {
        ViewSetLayoutAvailableSize(childView, 0, 0);
        ViewMeasure(childView);
    }
    else
    {
        ViewSetLayoutAvailableSize(childView, *available_w, *available_h);
        ViewMeasure(childView);
        ViewGetOccupySize(childView, &child_occupy_w, &child_occupy_h);

        if (layout->orientation == TYPE_LAYOUT_ORIENTATION_HORIZONTAL)
        {
            *available_w -= child_occupy_w;
            *wrap_w += child_occupy_w;
            if (child_occupy_h > *wrap_h)
                *wrap_h = child_occupy_h;
        }
        else if (layout->orientation == TYPE_LAYOUT_ORIENTATION_VERTICAL)
        {
            *available_h -= child_occupy_h;
            *wrap_h += child_occupy_h;
            if (child_occupy_w > *wrap_w)
                *wrap_w = child_occupy_w;
        }
    }

    return 0;
}

int LayoutMeasure(Layout *layout)
{
    if (!layout || !layout->layoutdata)
        return -1;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LayoutParams *layoutParams = &layout->params;

    if (layoutParams->available_w <= 0 || layoutParams->available_h <= 0)
    {
        layout->measured_w = 0;
        layout->measured_h = 0;
        return -1;
    }

    int layout_max_w = layoutParams->available_w - layoutParams->margin_left - layoutParams->margin_right;
    int layout_max_h = layoutParams->available_h - layoutParams->margin_top - layoutParams->margin_bottom;
    int layout_wrap_w = 0;
    int layout_wrap_h = 0;

    int children_max_w = layout_max_w - layout->padding_left - layout->padding_right;
    int children_max_h = layout_max_h - layout->padding_top - layout->padding_bottom;
    int children_wrap_w = 0;
    int children_wrap_h = 0;

    int n_remainings = LinkedListGetLength(layoutData->children);
    LinkedListEntry *entry = LinkedListHead(layoutData->children);

    // 先更新非MATH_PARENT的子控件，不然MATH_PARENT会占掉所有的空间
    while (entry)
    {
        View *childView = (View *)LinkedListGetEntryData(entry);
        LayoutParams *childParams = &childView->params;

        if (layout->orientation == TYPE_LAYOUT_ORIENTATION_FRAME ||
            (layout->orientation == TYPE_LAYOUT_ORIENTATION_HORIZONTAL && childParams->layout_w != TYPE_LAYOUT_SIZE_MATH_PARENT) ||
            (layout->orientation == TYPE_LAYOUT_ORIENTATION_VERTICAL && childParams->layout_h != TYPE_LAYOUT_SIZE_MATH_PARENT))
        {
            LayoutMeasureChild(layout, childView, &children_max_w, &children_max_h, &children_wrap_w, &children_wrap_h);
            n_remainings--;
        }

        entry = LinkedListNext(entry);
    }

    // 再更新MATH_PARENT的子控件，第一个MATH_PARENT会占用掉所有空间，以至于后面的视图将无法显示，暂时不支持weight属性
    if (n_remainings > 0)
    {
        entry = LinkedListHead(layoutData->children);
        while (entry)
        {
            View *childView = (View *)LinkedListGetEntryData(entry);
            LayoutParams *childParams = &childView->params;

            if ((layout->orientation == TYPE_LAYOUT_ORIENTATION_HORIZONTAL && childParams->layout_w == TYPE_LAYOUT_SIZE_MATH_PARENT) ||
                (layout->orientation == TYPE_LAYOUT_ORIENTATION_VERTICAL && childParams->layout_h == TYPE_LAYOUT_SIZE_MATH_PARENT))
            {
                LayoutMeasureChild(layout, childView, &children_max_w, &children_max_h, &children_wrap_w, &children_wrap_h);
            }

            entry = LinkedListNext(entry);
        }
    }

    layout_wrap_w = children_wrap_w + layout->padding_left + layout->padding_right;
    layout_wrap_h = children_wrap_h + layout->padding_top + layout->padding_bottom;

    layout->wrap_w = layout_wrap_w;
    layout->wrap_h = layout_wrap_h;

    if (layoutParams->layout_w == TYPE_LAYOUT_SIZE_MATH_PARENT)
        layout->measured_w = layout_max_w;
    else if (layoutParams->layout_w == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        layout->measured_w = layout_wrap_w;
    else
        layout->measured_w = layoutParams->layout_w;
    if (layout->measured_w > layout_max_w)
        layout->measured_w = layout_max_w;
    if (layout->measured_w < 0)
        layout->measured_w = 0;

    if (layoutParams->layout_h == TYPE_LAYOUT_SIZE_MATH_PARENT)
        layout->measured_h = layout_max_h;
    else if (layoutParams->layout_h == TYPE_LAYOUT_SIZE_WRAP_CONTENT)
        layout->measured_h = layout_wrap_h;
    else
        layout->measured_h = layoutParams->layout_h;
    if (layout->measured_h > layout_max_h)
        layout->measured_h = layout_max_h;
    if (layout->measured_h < 0)
        layout->measured_h = 0;

    return 0;
}

static int LayoutDrawChild(Layout *layout, View *childView, int *x, int *y, int min_x, int min_y, int max_x, int max_y)
{
    if (!layout || !childView)
        return 0;

    int sx = *x;
    int sy = *y;
    int next_x = *x;
    int next_y = *y;
    int child_occupy_w = 0;
    int child_occupy_h = 0;

    if (childView->visibility == TYPE_LAYOUT_VISIBILITY_GONE)
        return 0;

    ViewGetOccupySize(childView, &child_occupy_w, &child_occupy_h);

    // 帧布局或水平布局
    if ((layout->orientation == TYPE_LAYOUT_ORIENTATION_FRAME || layout->orientation == TYPE_LAYOUT_ORIENTATION_HORIZONTAL) && sy + child_occupy_h < max_y)
    {
        if ((layout->gravity & TYPE_LAYOUT_GRAVITY_BOTTOM)) // 子控件居底
            sy = max_y - child_occupy_h;
        if ((layout->gravity & TYPE_LAYOUT_GRAVITY_TOP)) // 子控件居顶
            ;
        else if ((layout->gravity & TYPE_LAYOUT_GRAVITY_CENTER_VERTICAL)) // 子控件垂直居中
            sy += (max_y - sy - child_occupy_h) / 2;
    }

    // 帧布局或垂直布局
    if ((layout->orientation == TYPE_LAYOUT_ORIENTATION_FRAME || layout->orientation == TYPE_LAYOUT_ORIENTATION_VERTICAL) && sx + child_occupy_w < max_x)
    {
        if (layout->gravity & TYPE_LAYOUT_GRAVITY_RIGHT) // 子控件居右
            sx = max_x - child_occupy_w;
        if ((layout->gravity & TYPE_LAYOUT_GRAVITY_LEFT)) // 子控件居左
            ;
        else if (layout->gravity & TYPE_LAYOUT_GRAVITY_CENTER_HORIZONTAL) // 子控件水平居中
            sx += (max_x - sx - child_occupy_w) / 2;
    }

    int dx = sx + child_occupy_w;
    int dy = sy + child_occupy_h;

    if (layout->orientation == TYPE_LAYOUT_ORIENTATION_HORIZONTAL)
    {
        // 水平布局: x 前进，y 不变
        next_x = dx;
        if (dx <= min_x) // 当前控件未在视图显示区域，跳过绘制
            goto NEXT;
        if (sx >= max_x) // 当前控件超出了视图显示区域，返回终止提示
            return -1;
    }
    else if (layout->orientation == TYPE_LAYOUT_ORIENTATION_VERTICAL)
    {
        // 垂直布局: y 前进，x 不变
        next_y = dy;
        if (dy <= min_y)
            goto NEXT;
        if (sy >= max_y)
            return -1;
    }

    ViewSetLayoutPosition(childView, sx, sy);
    if (childView->visibility == TYPE_LAYOUT_VISIBILITY_VISIBLE)
        ViewDraw(childView);

NEXT:
    *x = next_x;
    *y = next_y;

    return 0;
}

int LayoutDraw(Layout *layout)
{
    if (!layout || !layout->layoutdata)
        return -1;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LayoutParams *layoutParams = &layout->params;

    if (layout->measured_w <= 0 || layout->measured_h <= 0)
        return 0;

    int layout_x = layoutParams->layout_x + layoutParams->margin_left;
    int layout_y = layoutParams->layout_y + layoutParams->margin_top;
    int layout_w = layout->measured_w;
    int layout_h = layout->measured_h;

    int children_sx = layout_x + layout->padding_left;
    int children_sy = layout_y + layout->padding_top;
    int children_dx = layout_x + layout_w - layout->padding_right;
    int children_dy = layout_y + layout_h - layout->padding_bottom;
    int children_w = children_dx - children_sx;
    int children_h = children_dy - children_sy;

    if (layout->bg_color)
        GUI_DrawFillRectangle(layout_x, layout_y, layout_w, layout_h, layout->bg_color);

    GUI_SetClipping(children_sx, children_sy, children_w, children_h);

    int child_x = children_sx;
    int child_y = children_sy;

    LinkedListEntry *entry = LinkedListHead(layoutData->children);
    while (entry)
    {
        View *childView = (View *)LinkedListGetEntryData(entry);
        if (LayoutDrawChild(layout, childView, &child_x, &child_y, children_sx, children_sy, children_dx, children_dy) < 0)
            break;
        entry = LinkedListNext(entry);
    }

    GUI_UnsetClipping();

    return 0;
}

int LayoutAddView(Layout *layout, View *view)
{
    if (!layout || !layout->layoutdata || !view)
        return 0;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    if (LinkedListAdd(layoutData->children, view))
        return 1;

    return 0;
}

int LayoutAddViewAbove(Layout *layout, View *view, View *above)
{
    if (!layout || !layout->layoutdata || !view || !above)
        return 0;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LinkedListEntry *entry = LinkedListFindByData(layoutData->children, above);
    if (entry && LinkedListAddAbove(layoutData->children, entry, view))
        return 1;

    return 0;
}

int LayoutAddViewBelow(Layout *layout, View *view, View *below)
{
    if (!layout || !layout->layoutdata || !view || !below)
        return 0;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LinkedListEntry *entry = LinkedListFindByData(layoutData->children, below);
    if (entry && LinkedListAddBelow(layoutData->children, entry, view))
        return 1;

    return 0;
}

int LayoutRemoveView(Layout *layout, View *view)
{
    if (!layout || !layout->layoutdata || !view)
        return 0;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LinkedListEntry *entry = LinkedListFindByData(layoutData->children, view);
    if (entry && LinkedListRemove(layoutData->children, entry))
        return 1;

    return 0;
}

int LayoutEmptyViews(Layout *layout)
{
    if (!layout || !layout->layoutdata)
        return -1;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    LinkedListEmpty(layoutData->children);
    return 0;
}

int LayoutInit(Layout *layout)
{
    if (!layout)
        return -1;

    memset(layout, 0, sizeof(Layout));

    layout->layoutdata = calloc(1, sizeof(LayoutData));
    if (!layout->layoutdata)
        return -1;

    LayoutData *layoutData = (LayoutData *)layout->layoutdata;
    layoutData->children = NewLinkedList();
    if (!layoutData->children)
        goto FAILED;
    LinkedListSetFreeEntryDataCallback(layoutData->children, (void *)ViewFinish);

    layout->onMeasure = LayoutMeasure;
    layout->onDraw = LayoutDraw;
    layout->onFinish = LayoutDestroy;

    return 0;

FAILED:
    LayoutDeinit(layout);
    return -1;
}

int LayoutDeinit(Layout *layout)
{
    if (!layout)
        return -1;

    LayoutDestroyData((LayoutData *)layout->layoutdata);
    memset(layout, 0, sizeof(Layout));

    return 0;
}

Layout *NewLayout()
{
    Layout *layout = (Layout *)malloc(sizeof(Layout));
    if (!layout)
        return NULL;

    if (LayoutInit(layout) < 0)
    {
        free(layout);
        return NULL;
    }

    return layout;
}

int LayoutDestroy(Layout *layout)
{
    if (!layout)
        return -1;

    if (layout->dont_free)
        return 0;

    LayoutDestroyData((LayoutData *)layout->layoutdata);
    free(layout);

    return 0;
}
